package com.xiaowen.tool;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.context.i18n.LocaleContextHolder;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by xiabin on 2017/3/1.
 */
@Slf4j
public class HttpTool {

    /**
     * requestId
     */
    private static final String MDC_KEY_REQUESTID = "requestId";
    /**
     * request_id
     */
    private static final String MDC_KEY_REQUEST_ID = "request_id";

    /**
     * 向指定URL发送GET方法的请求
     *
     * @param url     发送请求的URL
     * @param headers http headers
     * @return URL 所代表远程资源的响应结果
     */
    public static String get(String url, Map<String, String> headers) throws IOException, HttpException {
        long beginTime = System.nanoTime();
        String result = "";
        BufferedReader in = null;
        URL realUrl = new URL(url);
        // 打开和URL之间的连接
        HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
        // 设置通用的请求属性
        connection.setRequestProperty("accept", "*/*");
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        connection.setConnectTimeout(30000);
        connection.setReadTimeout(30000);
        appendHeaders(connection, headers);
        // 建立实际的连接
        connection.connect();
        int code = connection.getResponseCode();
        if (code != 200) {
            throw new HttpException(code);
        }
        try {
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            log.error("httptool.get失败,url" + url + ",exception:" + e);
            e.printStackTrace();
            throw e;
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        log.info("请求url ： {} ,花费时间 : {}", url, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginTime));
        return result;
    }

    public static String get(String url) throws IOException, HttpException {
        return get(url, null);
    }

    private static void appendHeaders(URLConnection connection, Map<String, String> headers) {
        if (headers != null) {
            for (String key : headers.keySet()) {
                connection.setRequestProperty(key, headers.get(key));
            }
        }
        String lang = LocaleContextHolder.getLocale().toString();
        // log.info("language info is {}", lang);
        connection.setRequestProperty("lang", lang);
        connection.setRequestProperty("from_lang", lang);
    }

    public static String postByJson(String url, Object param) throws IOException, HttpException {
        ignoreSSL();
        return postByJson(url, param, null);
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String postByJson(String url, Object param, Map<String, String> headers) throws IOException, HttpException {
        long beginTime = System.nanoTime();
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        URL realUrl = new URL(url);
        // 打开和URL之间的连接
        HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
        // 设置通用的请求属性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencode");
        conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        if (param != null) {
            conn.setRequestProperty("Content-Type", "application/json");
            if (!(param instanceof String)) {
                if (param instanceof JSONObject) {
                    param = ((JSONObject) param).toJSONString();
                } else {
                    param = JSONObject.toJSONString(param);
                }
            }
        }

        appendHeaders(conn, headers);

        // 发送POST请求必须设置如下两行
        conn.setDoOutput(true);
        conn.setDoInput(true);
        // 获取URLConnection对象对应的输出流
        if (param != null) {
            out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
        }
        int code = conn.getResponseCode();
//        if (code != 200) {
//            throw new HttpException(code);
//        }
        try {
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        log.info("请求url ： {} ,花费时间 : {} : code {} ", url, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginTime), code);
        return result;
    }


    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String postByForm(String url, Object param, Map<String, String> headers) throws IOException, HttpException {
        long beginTime = System.nanoTime();
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        URL realUrl = new URL(url);
        // 打开和URL之间的连接
        HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
        // 设置通用的请求属性
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencode");
        conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        if (param != null) {
            if (!(param instanceof String)) {
                if (param instanceof JSONObject) {
                    param = ((JSONObject) param).toJSONString();
                } else {
                    param = JSONObject.toJSONString(param);
                }
            }
        }

        appendHeaders(conn, headers);

        // 发送POST请求必须设置如下两行
        conn.setDoOutput(true);
        conn.setDoInput(true);
        // 获取URLConnection对象对应的输出流
        if (param != null) {
            out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
        }
        int code = conn.getResponseCode();
//        if (code != 200) {
//            throw new HttpException(code);
//        }
        try {
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        log.info("请求url ： {} ,花费时间 : {} : code {} ", url, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginTime), code);
        return result;
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String postByMultipartForm(String url, Map<String, Object> param, Map<String, String> headers) throws IOException, HttpException {
        long start = System.currentTimeMillis();
        try {
            CloseableHttpClient client = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            param.forEach((k,v)->{
                if (v instanceof File){
                    builder.addBinaryBody(k, (File) v);
                }
                builder.addTextBody(k, String.valueOf(v)); // 可选参数
            });
            HttpEntity multipart = builder.build();
            httpPost.setEntity(multipart);

            HttpResponse response = client.execute(httpPost);
            long end = System.currentTimeMillis();
            int status = response.getStatusLine().getStatusCode();
            log.info("请求url：{}，结果状态：{}，耗时：{}毫秒。", url, status, (end - start));
            if (status >= 200 && status < 300) {
                HttpEntity entity = response.getEntity();
                return entity != null ? EntityUtils.toString(entity,"UTF-8") : null;
            }
        } catch (Exception e) {
            log.error("请求接口错误:{},{}",url,e);
        }
        return null;
    }

    public static String get(String url, Map<String, String> headers, Integer connectTimeout, Integer readTimeout) throws IOException, HttpException {
        String result = "";
        BufferedReader in = null;
        URL realUrl = new URL(url);
        // 打开和URL之间的连接
        HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
        // 设置通用的请求属性
        connection.setRequestProperty("accept", "*/*");
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        connection.setConnectTimeout(connectTimeout);
        connection.setReadTimeout(readTimeout);
        appendHeaders(connection, headers);
        // 建立实际的连接
        connection.connect();
        int code = connection.getResponseCode();
        if (code != 200) {
            throw new HttpException(code);
        }
        try {
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            log.error("httptool.get失败,url" + url + ",exception:" + e);
            e.printStackTrace();
            throw e;
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }


    public static void ignoreSSL() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            TrustManager[] trustManagers = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                        }

                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                    }
            };
            sslContext.init(null, trustManagers,new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e){
            log.info("ignoreSSLError:{}",e);
        }
    }

}
